#include <stdlib.h>           
#include <sys/ptrace.h>       
#include <sys/reg.h>          
#include <sys/syscall.h>      
#include <unistd.h>
#include <sys/wait.h>
#include <string.h>        

const int long_size = sizeof(long);


/* Pasa a MAYUSCULAS un string */
void upper(char *str)
{  
    int i;
    for(i = 0; i < strlen(str); i++) {
        str[i] = toupper(str[i]);
    }
}

void getdata(pid_t child, long addr, char *str, int len)
{  
    char *laddr;
    int i, j;
    union u {
            long val;
            char chars[long_size];
    }data;
    i = 0;
    j = len / long_size;
    laddr = str;
    
    // Mientras tenga que leer hago un PTRACE_PEEKDATA
    while(i < j) {
        data.val = ptrace(PTRACE_PEEKDATA,
                          child, addr + i * 4,
                          NULL);
		// Copio una porcion de memoria de tamaño lons_size apuntada por laddr y la guardo en data.chars					
        memcpy(laddr, data.chars, long_size);
        ++i;
        laddr += long_size;
    }
    j = len % long_size;
    if(j != 0) {
        data.val = ptrace(PTRACE_PEEKDATA,
                          child, addr + i * 4,
                          NULL);
        memcpy(laddr, data.chars, j);
    }
    str[len] = '\0';
}

void putdata(pid_t child, long addr, char *str, int len)
{  
    char *laddr;
    int i, j;
    union u {
            long val;
            char chars[long_size];
    }data;
    i = 0;
    j = len / long_size;
    laddr = str;
    // Voy escribiendo en addr con PTRACE_POOKEDATA
    while(i < j) {
        memcpy(data.chars, laddr, long_size);
        ptrace(PTRACE_POKEDATA, child,
               addr + i * 4, data.val);
        ++i;
        laddr += long_size;
    }
    j = len % long_size;
    if(j != 0) {
        memcpy(data.chars, laddr, j);
        ptrace(PTRACE_POKEDATA, child,
               addr + i * 4, data.val);
    }
}

int main(int argc, char* argv[]) {
   
    int child = fork();
    int status;
   
    if (child == -1) { perror("ERROR fork"); return 1; }
    if (child == 0) {

		// El proceso hijo solicita ser monitoreado haciendo una llamada a ptrace(PTRACE_TRACEME) 
        if (ptrace(PTRACE_TRACEME, 0, NULL, NULL)) {
            perror("ERROR child ptrace(PTRACE_TRACEME, ...)");
            exit(1);
        }
        
        // Tomo del argumento el código a ejecutar
        execvp(argv[1], argv+1);

        perror("ERROR child exec(...)"); exit(1);
    } else {
        while(1) {
            if (wait(&status) < 0) {
                perror("wait");
                break;
            }
            if (WIFEXITED(status))
                break; /* Proceso terminado */
              
			// En eax tenemos el nro de systemCall que esta intentando hacer el child
			// accedemos a ella mediante PTRACE_PEEKUSER
            int sysno = ptrace(PTRACE_PEEKUSER, child, 4*ORIG_EAX, NULL);

			//Si la systemCall es de escritura
            if( sysno == SYS_write) {
                   
					//Obtenemos la direccion de memoria y la longitud de la cadena a escribir
                    int addr = ptrace(PTRACE_PEEKUSER, child, 4*ECX, NULL);
                    int len = ptrace(PTRACE_PEEKUSER, child, 4*EDX, NULL);
              
					//Rreservo memoria para la cadena a escribir
                    char *str;
                    str = (char *)calloc((len+1), sizeof(char));
                   
					//Obtengo la cadena del hijo
                    getdata(child, addr, str, len);
                    //Paso a mayuscalas 
                    upper(str);
                    //Vuelvo a insertar la cadena
                    putdata(child, addr, str, len);
            }
            ptrace(PTRACE_SYSCALL, child, NULL, NULL); /* contina */
        }
        ptrace(PTRACE_DETACH, child, NULL, NULL);/*Liberamos al hijo*/
    }
    return 0;
}
